#include <libavformat/internal.h>
#include <libavformat/avformat.h>

#include <unistd.h>
#include <pthread.h>

typedef struct
{
    AVIOContext *client;
    AVFormatContext *out_ctx;
} h264_out_t;

void log_hex(AVPacket *pkt)
{
    for (size_t i = 0; i < pkt->size; i++)
    {
        printf("%x ", *(pkt->data + i));
    }
    printf("\n\n");
}

void *process_client(void *arg)
{
    h264_out_t *param = arg;

    AVIOContext *client = param->client;
    AVFormatContext *out_ctx = param->out_ctx;

    int ret;
    uint8_t buf[1024];
    // avio_handshake(client);

    AVInputFormat *in_fmt = av_find_input_format("h264");

    AVFormatContext *in_ctx = avformat_alloc_context();
    in_ctx->iformat = in_fmt;
    in_ctx->pb = client;
    if (avformat_open_input(&in_ctx, "", in_fmt, NULL) < 0)
    {
        av_log(client, AV_LOG_ERROR, "open input error");
    }

    av_dump_format(in_ctx, 0, NULL, 0);

    while (1)
    {
        AVPacket *pkt = av_packet_alloc();
        ret = av_read_frame(in_ctx, pkt);

        if (ret < 0)
        {
            av_log(in_ctx, AV_LOG_ERROR, "read frame error");
            break;
        }

        time_t now;
        time(&now);

        // log_hex(pkt);
        pkt->stream_index = 0;
        pkt->pts = now;
        pkt->dts = now;
        av_write_frame(out_ctx, pkt);
    }

    // while (1)
    // {
    //     ret = avio_read(client, buf, sizeof(buf));
    //     if (ret < 0)
    //     {
    //         usleep(3000);
    //         if (ret == AVERROR_EOF)
    //             break;
    //         av_log(client, AV_LOG_ERROR, "Error reading from input: %s.\n",
    //                av_err2str(ret));
    //         break;
    //     }
    // }
    return 0;
}

int main(int argc, char const *argv[])
{

    int ret;
    // open output
    AVFormatContext *out_ctx = NULL;
    AVDictionary *out_opts = NULL;
    av_dict_set(&out_opts, "listen", "2", 0);

    const char *filename = "rtmp://0.0.0.0:1935/stream/live";
    avformat_alloc_output_context2(&out_ctx, NULL, "flv", filename);

    AVOutputFormat *fmt = out_ctx->oformat;
    if (!(fmt->flags & AVFMT_NOFILE))
    {
        AVDictionary *opts = NULL;
        av_dict_set(&opts, "listen", "2", 0);

        ret = avio_open2(&out_ctx->pb, filename, AVIO_FLAG_WRITE, NULL, &opts);

        if (ret < 0)
        {
            fprintf(stderr, "Could not open '%s': %s\n", filename,
                    av_err2str(ret));
            return 1;
        }
    }
    // out_ctx->nb_streams = 1;
    AVStream *st;

    st = avformat_new_stream(out_ctx, NULL);
    st->id = 1;

    AVStreamInternal *in = st->internal;


    in->priv_pts = av_mallocz(sizeof(*st->internal->priv_pts));

    // AVStream *st1 = out_ctx->streams[0];

    // if (!st1->internal->priv_pts)
    //     st1->internal->priv_pts = av_mallocz(sizeof(*st1->internal->priv_pts));
    // if (!st1->internal->priv_pts)
    //     return AVERROR(ENOMEM);

    AVDictionary *in_opts = NULL;
    av_dict_set(&in_opts, "listen", "2", 0);
    AVIOContext *client = NULL, *server = NULL;
    avio_open2(&server, "tcp://0.0.0.0:9762", AVIO_FLAG_READ, NULL, &in_opts);

    pthread_t th_arr[1024];

    while (1)
    {
        ret = avio_accept(server, &client);
        if (ret < 0)
        {
            av_log(server, AV_LOG_ERROR, "accept error");
            continue;
        }

        h264_out_t param;
        param.client = client;
        param.out_ctx = out_ctx;

        pthread_t tid;
        int status = pthread_create(&tid, NULL, process_client, &param);
        pthread_detach(tid);
    }

    return 0;
}
